package com.modernyuanshiren;

import java.time.Duration;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Resolver<T extends IStatus<T, U>, U extends ISingleAction<T, U>> {
	private List<Solution<T, U>> solutions = new ArrayList<>();
	private List<Solution<T, U>> success = new ArrayList<>();
	private List<List<Solution<T, U>>> preSolutions = new ArrayList<>();
	private int generatingLevel = 1;
	private Duration total;
	private boolean needCheckSymmetry = false;

	public Resolver(T initStatus) {
		solutions.add(new Solution<T, U>(initStatus));
		preSolutions.add(solutions.stream().collect(Collectors.toList()));
		needCheckSymmetry = initStatus.needCheckSymmetry();
	}

	private void outputBest() {
		System.out
		.printf("=====\n全耗时%dms\n", total.toMillis());
		Entry<Integer, List<Solution<T, U>>> bestEntry = success.stream()
				.collect(Collectors.groupingBy(s -> s.getActions().size()))
				.entrySet().stream()
				.min(Comparator.comparingInt(t -> t.getKey())).get();
		System.out.println("最小步数：" + bestEntry.getKey());
		
		List<Solution<T, U>> best = bestEntry.getValue();
		if (best.size() > 1) {
			IntStream.range(0, best.size()).forEach(i -> {
				System.out.printf("Solution %d:\n", i + 1);
				best.get(i).output();
			});
		} else {
			System.out.println("Solution:");
			best.get(0).output();
		}
	}

	public void resolve() {
		total = Duration.ZERO;
		while (!solutions.isEmpty()) {
			generatingLevel++;
			List<Solution<T, U>> results = new ArrayList<>();

			System.out.printf("正在計算第%d次移动", generatingLevel - 1);
			LocalTime start = LocalTime.now();
			for (Solution<T, U> solution : solutions) {
				List<Solution<T, U>> nextLevel = solution.nextLevel();
				if(needCheckSymmetry && solution.isSymmetry()){					
					for(int i=nextLevel.size()-1;i>0;i--){
						Solution<T, U> solutionI = nextLevel.get(i);
						boolean has = IntStream.range(0, i).boxed().map(nextLevel::get).anyMatch(solutionI::checkSymmetry);
						if(has) {
							nextLevel.remove(i);
						}
					}
				}
				boolean hasEnd = false;
				List<Solution<T, U>> notEnd = new ArrayList<>();
				for (Solution<T, U> solution2 : nextLevel) {
					if (solution2.isEnd()) {
						success.add(solution2);
						hasEnd = true;
					} else {
						notEnd.add(solution2);
					}
				}
				if (!hasEnd) {
					if (generatingLevel < 3) {
						results.addAll(notEnd);
					} else {
						List<Solution<T, U>> grand = preSolutions.get(generatingLevel - 3);
						for (Solution<T, U> solution3 : notEnd) {
							if(!results.contains(solution3)) {
								if (results.stream().noneMatch(s->needCheckSymmetry &&solution3.checkSymmetry(s))) {
									if (grand.stream().noneMatch(s->solution3.equals(s)||(needCheckSymmetry &&solution3.checkSymmetry(s)))) {
										boolean add = true;
										if(generatingLevel>3){
											add = preSolutions.stream().limit(generatingLevel-3).flatMap(List::stream).noneMatch(solution3::equals);
										}
										if(add){
											results.add(solution3);
										}
									}
								}
							}
						}
					}
				} else {
					break;
				}
			}
			solutions.clear();
			//Debug
			if(generatingLevel>25){
				results.forEach(Solution::output);
			}
			solutions.addAll(results);
			preSolutions.add(results);
			LocalTime end = LocalTime.now();
			Duration oneTime = Duration.between(start, end);
			System.out.printf("%dms\n", oneTime.toMillis());
			total = total.plus(oneTime);
			if (!success.isEmpty()) {
				break;
			}
		}
		outputBest();
	}
}
